package se.mushroomwars.network.protocol;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


public class SendData {
	private Map<SocketChannel, ByteBuffer> map;

	// used in the server
	public SendData() {
		map = Collections
				.synchronizedMap(new HashMap<SocketChannel, ByteBuffer>());
		// this.players = players;
	}

	public ByteBuffer objectToByteBuffer(Object data) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutput out = new ObjectOutputStream(bos);
		out.writeObject(data);
		byte[] res = bos.toByteArray();
		out.close();
		bos.close();
		return ByteBuffer.wrap(res);
	}

	// add data to the send-buffer, will start to send from the send-buffer when
	// the key.isReadable() say so
	public boolean send(SocketChannel socket, Object data, int packageID) {
		synchronized (map) {

			if (!map.containsKey(socket))
				map.put(socket, null);

			ByteBuffer curHolder = map.get(socket);
			ByteBuffer newData = null;

			// if the conversion failed abort and return false
			try {
				newData = objectToByteBuffer(data);
			} catch (IOException e) {
				return false;
			}
			

			// add a package-header to the new data
			PackageHeader header = new PackageHeader(newData.capacity(),
					packageID);

			// if the buffer wasnt empty, append new data-package
			if (curHolder != null) {
				ByteBuffer buf = ByteBuffer.allocate(curHolder.capacity()
						+ newData.capacity() + PackageHeader.HEADER_LENGTH);
				curHolder.position(0);
				buf.put(curHolder);
				buf.put(header.encodeHeader());
				buf.put(newData);
				curHolder = buf;
				// System.out.println("adding message");
			}
			// the buffer was empty, add data
			else {
				curHolder = ByteBuffer.allocate(newData.capacity()
						+ PackageHeader.HEADER_LENGTH);
				curHolder.put(header.encodeHeader());
				curHolder.put(newData);
				// System.out.println("new message");
			}

			curHolder.position(0);
			map.put(socket, curHolder);
			// System.out.println("pos: " + curHolder.position() +
			// " Message added curHolder.cap: " + curHolder.capacity() +
			// " curHolder.limit: " + curHolder.limit());
			// everything is ok!
			return true;
		}
	}

	// send data from the send-buffer
	public boolean send(SocketChannel socket) throws IOException {
		synchronized (map) {
			// SocketChannel socket = (SocketChannel) key.channel();

			// nothing to do
			if (!map.containsKey(socket))
				return true;
			// map.put(key, new Holder());

			ByteBuffer curHolder = map.get(socket);
			// System.out.println("data in curHolder: " + curHolder.toString());

			// check if something went wrong with the socket
			try {
				int n = socket.write(curHolder);
				curHolder.position(n);
				// System.out.println(n);
			} catch (IOException e) {
				return false;
			}
			curHolder = curHolder.slice();
			if (curHolder.remaining() == 0)
				map.remove(socket);

			// everything seems okay
			return true;
		}
	}
}
